package cn.jingyuan.bee.utils;

import cn.jingyuan.bee.utils.lang.Assert;
import cn.jingyuan.bee.utils.map.MapUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 枚举工具类
 */
public class EnumUtils {

    /**
     * 指定类是否为 Enum 类
     *
     * @param clazz 类
     *
     * @return 是否为 Enum 类
     */
    public static boolean isEnum(Class<?> clazz) {
        Assert.notNull(clazz);
        return clazz.isEnum();
    }

    /**
     * 指定类是否为 Enum 类
     *
     * @param obj 类
     *
     * @return 是否为 Enum 类
     */
    public static boolean isEnum(Object obj) {
        Assert.notNull(obj);
        return obj.getClass().isEnum();
    }

    /**
     * Enum 对象转 String，调用 {@link Enum#name()} 方法
     *
     * @param e Enum
     *
     * @return name 值
     */
    public static String toString(Enum<?> e) {
        return null != e ? e.name() : null;
    }

    /**
     * 字符串转枚举，调用 {@link Enum#valueOf(Class, String)}
     *
     * @param <E> 枚举类型泛型
     * @param enumClass 枚举类
     * @param value 值
     *
     * @return 枚举值
     */
    public static <E extends Enum<E>> E fromString(Class<E> enumClass, String value) {
        return Enum.valueOf(enumClass, value);
    }

    /**
     * 字符串转枚举，调用 {@link Enum#valueOf(Class, String)}<br>
     * 如果无枚举值，返回默认值
     *
     * @param <E> 枚举类型泛型
     * @param enumClass 枚举类
     * @param value 值
     * @param defaultValue 无对应枚举值返回的默认值
     *
     * @return 枚举值
     */
    public static <E extends Enum<E>> E fromString(Class<E> enumClass, String value, E defaultValue) {
        return ObjectUtils.defaultIfNull(fromStringQuietly(enumClass, value), defaultValue);
    }

    /**
     * 字符串转枚举，调用 {@link Enum#valueOf(Class, String)}，转换失败返回 {@code null} 而非报错
     *
     * @param <E> 枚举类型泛型
     * @param enumClass 枚举类
     * @param value 值
     *
     * @return 枚举值
     */
    public static <E extends Enum<E>> E fromStringQuietly(Class<E> enumClass, String value) {
        if (null == enumClass || StringUtils.isBlank(value)) {
            return null;
        }

        try {
            return fromString(enumClass, value);
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    /**
     * 模糊匹配转换为枚举，给定一个值，匹配枚举中定义的所有字段名（包括 name 属性），一旦匹配到返回这个枚举对象，否则返回 null
     *
     * @param <E> 枚举类型
     * @param enumClass 枚举类
     * @param value 值
     *
     * @return 匹配到的枚举对象，未匹配到返回 null
     */
    public static <E extends Enum<E>> E likeValueOf(Class<E> enumClass, Object value) {
        if (value instanceof CharSequence) {
            value = value.toString().trim();
        }

        final Field[] fields = ReflectUtils.getFields(enumClass);
        final Enum<?>[] enums = enumClass.getEnumConstants();
        String fieldName;
        for (Field field : fields) {
            fieldName = field.getName();
            if (field.getType().isEnum() || "ENUM$VALUES".equals(fieldName) || "ordinal".equals(fieldName)) {
                // 跳过一些特殊字段
                continue;
            }
            for (Enum<?> enumObj : enums) {
                if (ObjectUtils.equal(value, ReflectUtils.getFieldValue(enumObj, field))) {
                    if (null == enumObj) {
                        return null;
                    }
                    return (E) enumObj;
                }
            }
        }
        return null;
    }

    /**
     * 枚举类中所有枚举对象的 name 列表
     *
     * @param clazz 枚举类
     *
     * @return name 列表
     */
    public static List<String> getNames(Class<? extends Enum<?>> clazz) {
        final Enum<?>[] enums = clazz.getEnumConstants();
        if (null == enums) {
            return Collections.emptyList();
        }
        final List<String> list = new ArrayList<>(enums.length);
        for (Enum<?> e : enums) {
            list.add(e.name());
        }
        return list;
    }

    /**
     * 获得枚举类中各枚举对象下指定字段的值
     *
     * @param clazz 枚举类
     * @param fieldName 字段名，最终调用 getXXX 方法
     *
     * @return 字段值列表
     */
    public static List<Object> getFieldValues(Class<? extends Enum<?>> clazz, String fieldName) {
        final Enum<?>[] enums = clazz.getEnumConstants();
        if (null == enums) {
            return Collections.emptyList();
        }
        final List<Object> list = new ArrayList<>(enums.length);
        for (Enum<?> e : enums) {
            list.add(ReflectUtils.getFieldValue(e, fieldName));
        }
        return list;
    }

    /**
     * 获得枚举类中所有的字段名<br>
     * 除用户自定义的字段名，也包括“name”字段，例如：
     *
     * <pre>
     *   EnumUtil.getFieldNames(Color.class) == ["name", "index"]
     * </pre>
     *
     * @param clazz 枚举类
     *
     * @return 字段名列表
     */
    public static List<String> getFieldNames(Class<? extends Enum<?>> clazz) {
        final List<String> names = new ArrayList<>();
        final Field[] fields = ReflectUtils.getFields(clazz);
        String name;
        for (Field field : fields) {
            name = field.getName();
            if (field.getType().isEnum() || name.contains("$VALUES") || "ordinal".equals(name)) {
                continue;
            }
            if (!names.contains(name)) {
                names.add(name);
            }
        }
        return names;
    }

    /**
     * 获取枚举字符串值和枚举对象的 Map 对应，使用 LinkedHashMap 保证有序<br>
     * 结果中键为枚举名，值为枚举对象
     *
     * @param <E> 枚举类型
     * @param enumClass 枚举类
     *
     * @return 枚举字符串值和枚举对象的 Map 对应，使用 LinkedHashMap 保证有序
     */
    public static <E extends Enum<E>> LinkedHashMap<String, E> getEnumMap(final Class<E> enumClass) {
        final LinkedHashMap<String, E> map = new LinkedHashMap();
        for (final E e : enumClass.getEnumConstants()) {
            map.put(e.name(), e);
        }
        return map;
    }

    /**
     * 获得枚举名对应指定字段值的 Map<br>
     * 键为枚举名，值为字段值
     *
     * @param clazz 枚举类
     * @param fieldName 字段名，最终调用 getXXX 方法
     *
     * @return 枚举名对应指定字段值的 Map
     */
    public static Map<String, Object> getNameFieldMap(Class<? extends Enum<?>> clazz, String fieldName) {
        final Enum<?>[] enums = clazz.getEnumConstants();
        if (null == enums) {
            return null;
        }
        final Map<String, Object> map = MapUtils.newHashMap(enums.length);
        for (Enum<?> e : enums) {
            map.put(e.name(), ReflectUtils.getFieldValue(e, fieldName));
        }
        return map;
    }

    /**
     * 判断某个值是存在枚举中
     *
     * @param <E> 枚举类型
     * @param enumClass 枚举类
     * @param val 需要查找的值
     *
     * @return 是否存在
     */
    public static <E extends Enum<E>> boolean contains(final Class<E> enumClass, String val) {
        return EnumUtils.getEnumMap(enumClass).containsKey(val);
    }

    /**
     * 判断某个值是不存在枚举中
     *
     * @param <E> 枚举类型
     * @param enumClass 枚举类
     * @param val 需要查找的值
     *
     * @return 是否不存在
     */
    public static <E extends Enum<E>> boolean notContains(final Class<E> enumClass, String val) {
        return !contains(enumClass, val);
    }

    /**
     * 忽略大小检查某个枚举值是否匹配指定值
     *
     * @param e 枚举值
     * @param val 需要判断的值
     *
     * @return 是非匹配
     */
    public static boolean equalsIgnoreCase(final Enum<?> e, String val) {
        return StringUtils.equalsIgnoreCase(toString(e), val);
    }

    /**
     * 检查某个枚举值是否匹配指定值
     *
     * @param e 枚举值
     * @param val 需要判断的值
     *
     * @return 是非匹配
     */
    public static boolean equals(final Enum<?> e, String val) {
        return StringUtils.equals(toString(e), val);
    }

}
